package com.specificgroup.jira.plugin.acpro.action;

import com.atlassian.plugin.webresource.WebResourceManager;
import com.opensymphony.util.TextUtils;
import com.specificgroup.jira.plugin.acpro.Constants;
import com.specificgroup.jira.plugin.acpro.exception.AcproException;
import com.specificgroup.jira.plugin.acpro.model.PagingInfo;
import com.specificgroup.jira.plugin.acpro.model.Settings;
import com.specificgroup.jira.plugin.acpro.model.jira.JiraIssueData;
import com.specificgroup.jira.plugin.acpro.model.acpro.AcproItemsPage;
import com.specificgroup.jira.plugin.acpro.service.AcproManager;
import com.specificgroup.jira.plugin.acpro.session.SessionTempData;
import com.specificgroup.jira.plugin.acpro.session.SessionUtil;
import com.specificgroup.jira.plugin.acpro.xml.response.generated.ItemType;
import org.apache.log4j.Logger;
import webwork.action.ServletActionContext;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;
import java.util.Comparator;

/**
 *
 * @author Ivan Holub
 */
public class SelectItemsAction extends AcproActionSupport {

    private static final String NEXT_ACTION = "AcproSelectJiraProject!default.jspa";

    private static final String RESOURCE_KEY = "com.specificgroup.jira.plugin.acpro.jira-acpro-plugin:acpro-resource-js-select-items";

    private static final String BUTTON_NUMBER_OF_ITEMS_ON_PAGE = "btnNumberOfItemsOnPage";

    private static final String INPUT_NUMBER_OF_ITEMS_ON_PAGE = "numberOfItemsOnPage";

    private static final String BUTTON_SELECT_PAGE = "btnSelectPage";

    private static final String INPUT_SELECT_PAGE = "pageNumber";

    private static final String BUTTON_PREVIOUS_PAGE = "btnPreviousPage";

    private static final String BUTTON_NEXT_PAGE = "btnNextPage";

    private static final int FIRST_PAGE = PagingInfo.FIRST_PAGE;

    private Logger log = Logger.getLogger(getClass());

    private AcproManager acproManager;

    private WebResourceManager webResourceManager;

    private PagingInfo pagingInfo;

    public PagingInfo getPagingInfo() {
        return pagingInfo;
    }

    public SelectItemsAction(AcproManager acproManager, WebResourceManager webResourceManager) {
        this.acproManager = acproManager;
        this.webResourceManager = webResourceManager;
    }

    @Override
    public String doDefault() throws Exception {
        if (!hasPermissions()) {
            return PERMISSION_VIOLATION_RESULT;
        }

        Map<String, ItemType> xmlItemsMap = new HashMap<String, ItemType>();
        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        sessionTempData.setXmlItemsMap(xmlItemsMap);

        webResourceManager.requireResource(RESOURCE_KEY);

        Settings settings = SessionUtil.getSettings();

        AcproItemsPage acproItemsPage;
        try {

            acproManager.buildAcproProjectInfo(settings);
            acproItemsPage = acproManager.getAcproItemsPage(settings, FIRST_PAGE, Constants.NUMBER_OF_ITEMS_ON_PAGE);

        } catch (IOException e) {
            log.error("Error", e);
            addErrorMessage(e.toString());
            return ERROR;
        } catch (AcproException e) {
            log.error("Error", e);
            addErrorMessage(e.getMessage());
            return ERROR;
        }

        Integer numberOfItemsOnPage = Constants.NUMBER_OF_ITEMS_ON_PAGE;
        Integer totalNumberOfItems = settings.getAcproProjectInfo().getItemsCount();
        Integer numberOfPages = (totalNumberOfItems + numberOfItemsOnPage - 1) / numberOfItemsOnPage;
        Integer pageNumber = FIRST_PAGE;
        Integer numberOfItemsSelected = 0;

        pagingInfo = new PagingInfo();
        pagingInfo.setAcproItemsPage(acproItemsPage);
        pagingInfo.setNumberOfItemsSelected(numberOfItemsSelected);
        pagingInfo.setNumberOfItemsOnPage(numberOfItemsOnPage);
        pagingInfo.setTotalNumberOfItems(totalNumberOfItems);
        pagingInfo.setNumberOfPages(numberOfPages);
        pagingInfo.setPageNumber(pageNumber);

        sessionTempData.setPagingInfo(pagingInfo);

        return super.doDefault();
    }

    @Override
    protected String doExecute() throws Exception {
        if (!hasPermissions()) {
            return PERMISSION_VIOLATION_RESULT;
        }
        
        Settings settings = SessionUtil.getSettings();
        HttpServletRequest request = ServletActionContext.getRequest();
        
        if (request.getParameter(BUTTON_NUMBER_OF_ITEMS_ON_PAGE) != null) {
            return doUpdateNumberOfItemsOnPage();
        }

        if (request.getParameter(BUTTON_SELECT_PAGE) != null) {
            return doSelectPage();
        }

        if (request.getParameter(BUTTON_PREVIOUS_PAGE) != null) {
            return doPreviousPage();
        }

        if (request.getParameter(BUTTON_NEXT_PAGE) != null) {
            return doNextPage();
        }

        storeSelectedItems();

        Map<String, ItemType> newXmlItemsMap = storeSelectedItems();

        acproManager.setAcproSelectedItems(settings, newXmlItemsMap);

        String redirect = getRedirect(NEXT_ACTION);
        return redirect;
    }

    private Map<String, ItemType> storeSelectedItems() {
        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        Map<String, ItemType> sessionItemsMap = sessionTempData.getXmlItemsMap();
        //copy of xmlItemsMap 
        Map<String, ItemType> retValue = new HashMap<String, ItemType>(sessionItemsMap);
        
        String [] selectedItems = request.getParameterValues("selectedItems");
        Set<String> acproItemsKeys = new HashSet<String>();
        if (selectedItems != null) {
            acproItemsKeys.addAll(Arrays.asList(selectedItems));
        }

        PagingInfo sessionPagingInfo = sessionTempData.getPagingInfo();
        List<ItemType> xmlItems = sessionPagingInfo.getAcproItemsPage().getXmlItems();
        for (ItemType item: xmlItems) {
            String key = item.getItemKey();
            if (acproItemsKeys.contains(key)) {
                retValue.put(key, item);
            } else {
                retValue.remove(key);
            }
        }
        return retValue;
    }

    private String doUpdateNumberOfItemsOnPage() {

        webResourceManager.requireResource(RESOURCE_KEY);

        Settings settings = SessionUtil.getSettings();
        HttpServletRequest request = ServletActionContext.getRequest();
        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        PagingInfo sessionPagingInfo = sessionTempData.getPagingInfo();

        String numberOfItemsOnPageStr = request.getParameter(INPUT_NUMBER_OF_ITEMS_ON_PAGE);
        Integer numberOfItemsOnPage;
        try {
            numberOfItemsOnPage = Integer.valueOf(numberOfItemsOnPageStr);
        } catch (NumberFormatException e) {

            this.pagingInfo = sessionPagingInfo;
            
            addErrorMessage(getText("error.numberOfItemsOnPage", TextUtils.htmlEncode(numberOfItemsOnPageStr)));
            return ERROR;
        }

        if (!(numberOfItemsOnPage > 0)) {
            this.pagingInfo = sessionPagingInfo;
            addErrorMessage(getText("error.numberOfItemsOnPage", numberOfItemsOnPage));
            return ERROR;
        }

        Map<String, ItemType> newXmlItemsMap = storeSelectedItems();

        AcproItemsPage acproItemsPage;
        try {

            acproItemsPage = acproManager.getAcproItemsPage(settings, FIRST_PAGE, numberOfItemsOnPage);

        } catch (IOException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.toString());
            return ERROR;
        } catch (AcproException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.getMessage());
            return ERROR;
        }

        pagingInfo = sessionPagingInfo;
        Integer totalNumberOfItems = pagingInfo.getTotalNumberOfItems();
        Integer numberOfPages = (totalNumberOfItems + numberOfItemsOnPage - 1) / numberOfItemsOnPage;
        Integer pageNumber = FIRST_PAGE;

        pagingInfo.setAcproItemsPage(acproItemsPage);
        pagingInfo.setNumberOfItemsOnPage(numberOfItemsOnPage);
        pagingInfo.setNumberOfPages(numberOfPages);
        pagingInfo.setPageNumber(pageNumber);

        calculateNumberOfSelectedItems(newXmlItemsMap, pagingInfo);
        sessionTempData.setXmlItemsMap(newXmlItemsMap);

        return INPUT;
    }

    private void calculateNumberOfSelectedItems(Map<String, ItemType> xmlItemsMap, PagingInfo pagingInfo) {
        Integer numberOfItemsSelected = xmlItemsMap.size();
        pagingInfo.setNumberOfItemsSelected(numberOfItemsSelected);
    }

    private String doSelectPage() {

        webResourceManager.requireResource(RESOURCE_KEY);

        Settings settings = SessionUtil.getSettings();
        HttpServletRequest request = ServletActionContext.getRequest();
        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        PagingInfo sessionPagingInfo = sessionTempData.getPagingInfo();

        String pageNumberStr = request.getParameter(INPUT_SELECT_PAGE);
        Integer pageNumber;
        try {
            pageNumber = Integer.valueOf(pageNumberStr);
        } catch (NumberFormatException e) {

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(getText("error.pageNumber", TextUtils.htmlEncode(pageNumberStr)));
            return ERROR;
        }

        if (!(pageNumber > 0)) {
            this.pagingInfo = sessionPagingInfo;
            addErrorMessage(getText("error.pageNumber", pageNumber));
            return ERROR;
        }

        Integer numberOfItemsOnPage = sessionPagingInfo.getNumberOfItemsOnPage();

        Map<String, ItemType> newXmlItemsMap = storeSelectedItems();

        AcproItemsPage acproItemsPage;
        try {

            acproItemsPage = acproManager.getAcproItemsPage(settings, pageNumber, numberOfItemsOnPage);

        } catch (IOException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.toString());
            return ERROR;
        } catch (AcproException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.getMessage());
            return ERROR;
        }

        pagingInfo = sessionPagingInfo;

        pagingInfo.setAcproItemsPage(acproItemsPage);
        pagingInfo.setPageNumber(pageNumber);

        calculateNumberOfSelectedItems(newXmlItemsMap, pagingInfo);
        sessionTempData.setXmlItemsMap(newXmlItemsMap);

        return INPUT;
    }

    private String doPreviousPage() {

        webResourceManager.requireResource(RESOURCE_KEY);

        Settings settings = SessionUtil.getSettings();
        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        PagingInfo sessionPagingInfo = sessionTempData.getPagingInfo();

        Integer pageNumber = sessionPagingInfo.getPageNumber();
        pageNumber--;
        Integer numberOfItemsOnPage = sessionPagingInfo.getNumberOfItemsOnPage();

        Map<String, ItemType> newXmlItemsMap = storeSelectedItems();

        AcproItemsPage acproItemsPage;
        try {

            acproItemsPage = acproManager.getAcproItemsPage(settings, pageNumber, numberOfItemsOnPage);

        } catch (IOException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.toString());
            return ERROR;
        } catch (AcproException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.getMessage());
            return ERROR;
        }

        pagingInfo = sessionPagingInfo;
        pagingInfo.setAcproItemsPage(acproItemsPage);
        pagingInfo.setPageNumber(pageNumber);

        calculateNumberOfSelectedItems(newXmlItemsMap, pagingInfo);
        sessionTempData.setXmlItemsMap(newXmlItemsMap);

        return INPUT;
    }

    private String doNextPage() {

        webResourceManager.requireResource(RESOURCE_KEY);

        Settings settings = SessionUtil.getSettings();
        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        PagingInfo sessionPagingInfo = sessionTempData.getPagingInfo();

        Integer pageNumber = sessionPagingInfo.getPageNumber();
        pageNumber++;
        Integer numberOfItemsOnPage = sessionPagingInfo.getNumberOfItemsOnPage();

        Map<String, ItemType> newXmlItemsMap = storeSelectedItems();

        AcproItemsPage acproItemsPage;
        try {

            acproItemsPage = acproManager.getAcproItemsPage(settings, pageNumber, numberOfItemsOnPage);

        } catch (IOException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.toString());
            return ERROR;
        } catch (AcproException e) {
            log.error("Error", e);

            this.pagingInfo = sessionPagingInfo;

            addErrorMessage(e.getMessage());
            return ERROR;
        }

        pagingInfo = sessionPagingInfo;
        pagingInfo.setAcproItemsPage(acproItemsPage);
        pagingInfo.setPageNumber(pageNumber);

        calculateNumberOfSelectedItems(newXmlItemsMap, pagingInfo);
        sessionTempData.setXmlItemsMap(newXmlItemsMap);

        return INPUT;
    }

    public boolean isItemSelected(String itemKey) {
        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        Map<String, ItemType> sessionItemsMap = sessionTempData.getXmlItemsMap();
        boolean retValue = sessionItemsMap.containsKey(itemKey);
        return retValue;
    }
}
